#ifndef JEGEO_COMMON_H
#define JEGEO_COMMON_H

#include "jegeo/math.hpp"
#include "jegeo/model.hpp"

namespace jegeo {

namespace jemath = jegeo::math;

/****** point functions *******/
namespace point {
template<typename T>
inline Point<T> make(const T &x, const T &y) noexcept
{
    return std::tuple<T, T>(x, y);
}

template<typename T>
inline Point<T> make_zero() noexcept
{
    return std::tuple<T, T>(0, 0);
}

template<typename T>
inline void set(Point<T> &point, const T &x, const T &y) noexcept
{
    std::get<0>(point) = x;
    std::get<1>(point) = y;
}

template<typename T>
inline T get_x(const Point<T> &point) noexcept
{
    return std::get<0>(point);
}

template<typename T>
inline T get_y(const Point<T> &point) noexcept
{
    return std::get<1>(point);
}

template<typename T>
inline void set_x(Point<T> &point, const T &x) noexcept
{
    std::get<0>(point) = x;
}

template<typename T>
inline void set_y(Point<T> &point, const T &y) noexcept
{
    std::get<1>(point) = y;
}

template<typename T>
inline bool is_equal(const T &px1, const T &py1, const T &px2, const T &py2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return jemath::fuzzy_equal(px1, px2, epsilon) && jemath::fuzzy_equal(py1, py2, epsilon);
}

template<typename T>
inline bool not_equal(const T &px1, const T &py1, const T &px2, const T &py2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return jemath::fuzzy_notequal(px1, px2, epsilon) || jemath::fuzzy_notequal(py1, py2, epsilon);
}

template<typename T>
inline bool is_equal(const Point<T> &p1, const Point<T> &p2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return jemath::fuzzy_equal(std::get<0>(p1), std::get<0>(p2), epsilon) && jemath::fuzzy_equal(std::get<1>(p1), std::get<1>(p2), epsilon);
}

template<typename T>
inline bool not_equal(const Point<T> &p1, const Point<T> &p2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return jemath::fuzzy_notequal(std::get<0>(p1), std::get<0>(p2), epsilon) || jemath::fuzzy_notequal(std::get<1>(p1), std::get<1>(p2), epsilon);
}
} // namespace point

/****** circle functions *******/
namespace circle {
template<typename T>
inline Circle<T> make(const T &cx, const T &cy, const T &r) noexcept
{
    return std::tuple<T, T, T>(cx, cy, r);
}
template<typename T>
inline Circle<T> make(const Point<T> p, const T &r) noexcept
{
    return std::tuple<T, T, T>(std::get<0>(p), std::get<1>(p), r);
}

template<typename T>
inline Point<T> get_pc(const Circle<T> &circle) noexcept
{
    return point::make(std::get<0>(circle), std::get<1>(circle));
}

template<typename T>
inline T get_radius(const Circle<T> &circle) noexcept
{
    return std::get<2>(circle);
}
} // namespace circle

/****** line functions *******/
namespace line {
template<typename T>
inline Line<T> make(const T &x1, const T &y1, const T &x2, const T &y2) noexcept
{
    return std::tuple<T, T, T, T>(x1, y1, x2, y2);
}

template<typename T>
inline Line<T> make(const Point<T> &p1, const Point<T> &p2) noexcept
{
    return std::tuple<T, T, T, T>(std::get<0>(p1), std::get<1>(p1), std::get<0>(p2), std::get<1>(p2));
}

template<typename T>
inline Line<T> make_zero() noexcept
{
    return std::tuple<T, T, T, T>(0, 0, 0, 0);
}

template<typename T>
inline void set(Line<T> &line, const T &x1, const T &y1, const T &x2, const T &y2) noexcept
{
    std::get<0>(line) = x1;
    std::get<1>(line) = y1;
    std::get<2>(line) = x2;
    std::get<3>(line) = y2;
}

template<typename T>
inline void set_ps(Line<T> &line, const T &x, const T &y) noexcept
{
    std::get<0>(line) = x;
    std::get<1>(line) = y;
}

template<typename T>
inline void set_ps(Line<T> &line, const Point<T> &p) noexcept
{
    std::get<0>(line) = std::get<0>(p);
    std::get<1>(line) = std::get<1>(p);
}

template<typename T>
inline void set_pe(Line<T> &line, const T &x, const T &y) noexcept
{
    std::get<2>(line) = x;
    std::get<3>(line) = y;
}

template<typename T>
inline void set_pe(Line<T> &line, const Point<T> &p) noexcept
{
    std::get<2>(line) = std::get<0>(p);
    std::get<3>(line) = std::get<1>(p);
}




template<typename T>
inline Point<T> get_ps(const Line<T> &line) noexcept
{
    return point::make(std::get<0>(line), std::get<1>(line));
}

template<typename T>
inline Point<T> get_pe(const Line<T> &line) noexcept
{
    return point::make(std::get<2>(line), std::get<3>(line));
}

template<typename T>
inline T get_length(const Line<T> &line) noexcept
{
    return jemath::jehypot<T>(std::get<2>(line) - std::get<0>(line), std::get<3>(line) - std::get<1>(line));
}
} // namespace line

/****** segment functions *******/
namespace segment {
template<typename T>
inline Segment<T> make(const T &xs, const T &ys, const T &xe, const T &ye) noexcept
{
    return std::tuple<T, T, T, T>(xs, ys, xe, ye);
}

template<typename T>
inline Segment<T> make(const Point<T> &p1, const Point<T> &p2) noexcept
{
    return std::tuple<T, T, T, T>(std::get<0>(p1), std::get<1>(p1), std::get<0>(p2), std::get<1>(p2));
}

template<typename T>
inline Point<T> get_ps(const Segment<T> &segment) noexcept
{
    return point::make(std::get<0>(segment), std::get<1>(segment));
}

template<typename T>
inline Point<T> get_pe(const Segment<T> &segment) noexcept
{
    return point::make(std::get<2>(segment), std::get<3>(segment));
}
} // namespace segment

/****** rect functions *******/
namespace rect {
template<typename T>
inline Rect<T> make(const T &x1, const T &y1, const T &x2, const T &y2) noexcept
{
    return std::tuple<T, T, T, T>(x1, y1, x2, y2);
}

template<typename T>
inline Rect<T> make(const Point<T> &p1, const Point<T> &p2)
{
    return std::tuple<T, T, T, T>(std::get<0>(p1), std::get<1>(p1), std::get<0>(p2), std::get<1>(p2));
}

template<typename T>
inline Rect<T> make_zero() noexcept
{
    return std::tuple<T, T, T, T>(0, 0, 0, 0);
}

template<typename T>
inline Rect<T> make_null() noexcept
{
    return std::tuple<T, T, T, T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::max(), std::numeric_limits<T>::min(), std::numeric_limits<T>::min());
}

template<typename T>
inline Rect<T> make_infinite() noexcept
{
    return std::tuple<T, T, T, T>(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max(), std::numeric_limits<T>::max());
}

template<typename T>
inline Rect<T> make_from_points(const PointList<T> &points) noexcept
{
    if (points.empty())
        return rect::make_zero<T>();
    T minx = std::numeric_limits<T>::max();
    T miny = std::numeric_limits<T>::max();
    T maxx = std::numeric_limits<T>::min();
    T maxy = std::numeric_limits<T>::min();
    for (const auto &p : points) {
        minx = std::min(minx, std::get<0>(p));
        miny = std::min(miny, std::get<1>(p));
        maxx = std::max(maxx, std::get<0>(p));
        maxy = std::max(maxy, std::get<1>(p));
    }
    return rect::make(minx, miny, maxx, maxy);
}

template<typename T>
inline bool is_empty(const Rect<T> &rect) noexcept
{
    return std::get<0>(rect) >= std::get<2>(rect) || std::get<1>(rect) >= std::get<3>(rect);
}

template<typename T>
inline bool is_null(const Rect<T> &rect) noexcept
{
    return std::get<0>(rect) > std::get<2>(rect) || std::get<1>(rect) > std::get<3>(rect);
}

template<typename T>
inline Point<T> get_p1(const Rect<T> &rect) noexcept
{
    return point::make(std::get<0>(rect), std::get<1>(rect));
}

template<typename T>
inline Point<T> get_p2(const Rect<T> &rect) noexcept
{
    return point::make(std::get<2>(rect), std::get<3>(rect));
}

template<typename T>
inline T get_area(const Rect<T> &rect) noexcept
{
    return jemath::jeabs(jemath::jemul(std::get<2>(rect) - std::get<0>(rect), std::get<3>(rect) - std::get<1>(rect)));
}

template<typename T>
inline Point<T> get_center(const Rect<T> &rect) noexcept
{
    return point::make(jemath::roundcast<T, jereal>(static_cast<jereal>(std::get<0>(rect) + std::get<2>(rect)) * static_cast<jereal>(0.5)),
                       jemath::roundcast<T, jereal>(static_cast<jereal>(std::get<1>(rect) + std::get<3>(rect)) * static_cast<jereal>(0.5)));
}

} // namespace rect

/****** arc functions *******/
namespace arc {

template<typename T>
inline Arc<T> make(const T &xs, const T &ys, const T &xe, const T &ye, const T &xc, const T &yc, bool cw) noexcept
{
    return std::tuple<T, T, T, T, T, T, bool>(xs, ys, xe, ye, xc, yc, cw);
}

template<typename T>
inline Arc<T> make(const Point<T> &ps, const Point<T> &pe, const Point<T> &pc, bool cw) noexcept
{
    return std::tuple<T, T, T, T, T, T, bool>(std::get<0>(ps), std::get<1>(ps), std::get<0>(pe), std::get<1>(pe), std::get<0>(pc), std::get<1>(pc), cw);
}

template<typename T>
inline Arc<T> make(const Point<T> &ps, const Point<T> &pe, jereal angle) noexcept
{
    bool cw = angle < 0;
    T xc, yc;
    jemath::arc_center_from_angle(std::get<0>(ps), std::get<1>(ps), std::get<0>(pe), std::get<1>(pe), angle, xc, yc);
    return arc::make(std::get<0>(ps), std::get<1>(ps), std::get<0>(pe), std::get<1>(pe), xc, yc, cw);
}

template<typename T>
inline Point<T> get_ps(const Arc<T> &arc) noexcept
{
    return point::make(std::get<0>(arc), std::get<1>(arc));
}

template<typename T>
inline Point<T> get_pe(const Arc<T> &arc) noexcept
{
    return point::make(std::get<2>(arc), std::get<3>(arc));
}

template<typename T>
inline Point<T> get_pc(const Arc<T> &arc) noexcept
{
    return point::make(std::get<4>(arc), std::get<5>(arc));
}

template<typename T>
inline bool get_cw(const Arc<T> &arc) noexcept
{
    return std::get<6>(arc);
}

template<typename T>
inline PointList<T> get_bound_points(const Arc<T> &arc) noexcept
{
    return jemath::arc_bound_points(std::get<0>(arc), std::get<1>(arc), std::get<2>(arc), std::get<3>(arc), std::get<4>(arc), std::get<5>(arc), std::get<6>(arc));
}

template<typename T>
inline T get_radius(const Arc<T> &arc) noexcept
{
    return jemath::jehypot<T>(std::get<4>(arc) - std::get<0>(arc), std::get<5>(arc) - std::get<1>(arc));
}

template<typename T>
inline void set_cw(Arc<T> arc, bool cw) noexcept {
    std::get<6>(arc) = cw;
}

} // namespace arc

/****** archedsegment functions *******/
namespace edge {

template<typename T>
inline Edge<T> make(const T &xs, const T &ys, const T &xe, const T &ye, jereal angle) noexcept
{
    return std::tuple<T, T, T, T, jereal>(xs, ys, xe, ye, angle);
}

template<typename T>
inline Edge<T> make(const Vertex<T> &v1, const Vertex<T> &v2) noexcept
{
    return std::tuple<T, T, T, T, jereal>(std::get<0>(v1), std::get<1>(v1), std::get<0>(v2), std::get<1>(v2), std::get<2>(v2));
}

template<typename T>
inline Point<T> get_ps(const Edge<T> &edge) noexcept
{
    return point::make(std::get<0>(edge), std::get<1>(edge));
}

template<typename T>
inline Point<T> get_pe(const Edge<T> &edge) noexcept
{
    return point::make(std::get<2>(edge), std::get<3>(edge));
}

template<typename T>
inline jereal get_angle(const Edge<T> &edge) noexcept
{
    return std::get<4>(edge);
}

template<typename T>
inline bool is_line(const Edge<T> &edge) noexcept
{
    return std::get<4>(edge) == 0;
}

template<typename T>
inline bool is_arc(const Edge<T> &edge) noexcept
{
    return std::get<4>(edge) != 0;
}

template<typename T>
inline bool is_circle(const Edge<T> &edge) noexcept
{
    return std::get<4>(edge) == 360 || std::get<4>(edge) == -360;
}

template<typename T>
inline PointList<T> get_bound_points(const Edge<T> &edge) noexcept
{
    PointList<T> res;
    if (edge::is_line(edge)) {
        res.push_back(edge::get_ps(edge));
        res.push_back(edge::get_pe(edge));
    } else if (edge::is_arc(edge)) {
        auto points = jemath::arc_bound_points(std::get<0>(edge), std::get<1>(edge), std::get<2>(edge), std::get<3>(edge), std::get<4>(edge));
        res.insert(res.end(), points.begin(), points.end());
    } else if (edge::is_circle(edge)) {
        T xs = std::get<0>(edge), ys = std::get<1>(edge);
        T xc = std::get<2>(edge), yc = std::get<3>(edge);
        T r = jemath::jehypot(xc - xs, yc - ys);
        res.push_back(point::make(xc - r, yc - r));
        res.push_back(point::make(xc + r, yc + r));
    }
    return res;
}

template<typename T>
inline Rect<T> get_bound_rect(const Edge<T> &edge) noexcept
{
    auto points = edge::get_bound_points(edge);
    return rect::make_from_points(points);
}

template<typename T>
inline void reverse(Edge<T> &edge) noexcept
{
    std::swap(std::get<0>(edge), std::get<2>(edge));
    std::swap(std::get<1>(edge), std::get<3>(edge));
    std::get<4>(edge) = -std::get<4>(edge);
}
} // namespace edge

/****** vertex functions *******/
namespace vertex {
template<typename T>
inline Vertex<T> make_moveto(const T &x, const T &y) noexcept
{
    return std::tuple<T, T, jereal>(x, y, std::numeric_limits<jereal>::quiet_NaN());
}

template<typename T>
inline Vertex<T> make_lineto(const T &x, const T &y) noexcept
{
    return std::tuple<T, T, jereal>(x, y, 0);
}

template<typename T>
inline Vertex<T> make_arcto(const T &x, const T &y, jereal angle) noexcept
{
    return std::tuple<T, T, jereal>(x, y, angle);
}

template<typename T>
inline Point<T> get_pos(const Vertex<T> &vertex) noexcept
{
    return std::tuple<T, T>(std::get<0>(vertex), std::get<1>(vertex));
}

template<typename T>
inline jereal get_angle(const Vertex<T> &vertex) noexcept
{
    return std::get<2>(vertex);
}

template<typename T>
inline bool is_moveto(const Vertex<T> &v) noexcept
{
    return std::isnan(std::get<2>(v));
}

template<typename T>
inline bool is_lineto(const Vertex<T> &v) noexcept
{
    return std::get<2>(v) == 0;
}

template<typename T>
inline bool is_arcto(const Vertex<T> &v) noexcept
{
    return std::get<2>(v) != 0 && std::get<2>(v) != 360 && std::get<2>(v) != -360;
}

template<typename T>
inline bool is_circle(const Vertex<T> &v) noexcept
{
    return std::get<2>(v) == 360 || std::get<2>(v) == -360;
}

template<typename T>
inline bool pos_is_equal(const Vertex<T> &v1, const Vertex<T> &v2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return point::is_equal(std::get<0>(v1), std::get<1>(v1), std::get<0>(v2), std::get<1>(v2), epsilon);
}

template<typename T>
inline bool pos_not_equal(const Vertex<T> &v1, const Vertex<T> &v2, const T &epsilon = jemath::Epsilon<T>()) noexcept
{
    return point::not_equal(std::get<0>(v1), std::get<1>(v1), std::get<0>(v2), std::get<1>(v2), epsilon);
}

template<typename T>
inline void set_as_moveto(Vertex<T> &v) noexcept
{
    std::get<2>(v) = std::numeric_limits<jereal>::quiet_NaN();
}
} // namespace vertex

/****** polyline functions *******/
namespace polyline {
template<typename T>
inline void set_hole(Polyline<T> &polyline, bool hole) noexcept;

template<typename T>
inline void move_to(Polyline<T> &polyline, const T &x, const T &y) noexcept;

template<typename T>
inline void line_to(Polyline<T> &polyline, const T &x, const T &y) noexcept;

template<typename T>
inline void arc_to(Polyline<T> &polyline, const T &x, const T &y, jereal angle) noexcept;

template<typename T>
inline void arc_to(Polyline<T> &polyline, const T &xe, const T &ye, const T &xc, const T &yc, bool cw) noexcept;

template<typename T>
inline void closepath(Polyline<T> &polyline) noexcept;

template<typename T>
inline Polyline<T> make(bool ishole = false) noexcept
{
    return std::tuple<VertexList<T>, bool>(VertexList<T>(), ishole);
}

template<typename T>
inline Polyline<T> make_circle(const T &cx, const T &cy, const T &cr, bool cw = false, bool ishole = false) noexcept
{
    Polyline<T> polyline;
    std::get<1>(polyline) = ishole;
    move_to(polyline, cx + cr, cy);
    arc_to(polyline, cx + cr, cy, cx, cy, cw);
    return polyline;
}

template<typename T>
inline Polyline<T> make_rect(const T &x1, const T &y1, const T &x2, const T &y2, bool ishole = false) noexcept
{
    Polyline<T> polyline;
    std::get<1>(polyline) = ishole;
    move_to(polyline, x1, y1);
    line_to(polyline, x2, y1);
    line_to(polyline, x2, y2);
    line_to(polyline, x1, y2);
    closepath(polyline);
    return polyline;
}

template<typename T>
Polyline<T> make_from_linestoker(const Line<T> &line, const T &half_width, int cap)
{
    T width = half_width;
    T sx, sy, ex, ey;
    if (std::get<0>(line) <= std::get<2>(line)) {
        sx = std::get<0>(line);
        sy = std::get<1>(line);
        ex = std::get<2>(line);
        ey = std::get<3>(line);
    } else {
        sx = std::get<2>(line);
        sy = std::get<3>(line);
        ex = std::get<0>(line);
        ey = std::get<1>(line);
    }

    T lineLen = line::get_length(line);
    T vx = ex - sx;
    T vy = ey - sy;

    // 判断起点和终点是否重合
    if (jemath::fuzzy_iszero(vx) && jemath::fuzzy_iszero(vy)) {
        Polyline<T> polyline;
        if (cap == CAP_STYLE_ROUND) {
            polyline = jegeo::polyline::make_circle(sx, sy, half_width, false, false);
        } else if (cap == CAP_STYLE_SQUARE) {
            polyline = jegeo::polyline::make_rect(sx - half_width, sy - half_width, sx + half_width, sy + half_width, false);
        }
        return polyline;
    }

    jereal uvx = static_cast<jereal>(vx) / static_cast<jereal>(lineLen);
    jereal uvy = static_cast<jereal>(vy) / static_cast<jereal>(lineLen);

    // 将得到的的直线的单位向量逆时针选择 90 度, 可以得到 s 点到第一个点的向量, 从而可以计算出第一个点的坐标
    T vx1 = jemath::roundcast<T, jereal>(-uvy * static_cast<jereal>(width));
    T vy1 = jemath::roundcast<T, jereal>(uvx * static_cast<jereal>(width));
    T x1 = sx + vx1;
    T y1 = sy + vy1;

    // 和第一个点对称的就是第二个点, 计算出第二个对应的向量和坐标
    T vx2 = -vx1;
    T vy2 = -vy1;
    T x2 = sx + vx2;
    T y2 = sy + vy2;

    // 通过第二个向量和直线向量可以得到 s 点到第三个点的向量, 从而计算出第三个点的坐标
    T vx3 = vx2 + vx;
    T vy3 = vy2 + vy;
    T x3 = sx + vx3;
    T y3 = sy + vy3;

    // 通过第一个向量和直线向量可以得到 s 点到第四个点的向量, 从而计算出第四个点的坐标
    T vx4 = vx1 + vx;
    T vy4 = vy1 + vy;
    T x4 = sx + vx4;
    T y4 = sy + vy4;

    Polyline<T> polyline;
    if (cap == CAP_STYLE_ROUND) {
        set_hole(polyline, false);
        move_to(polyline, x1, y1);
        arc_to(polyline, x2, y2, sx, sy, false);
        line_to(polyline, x3, y3);
        arc_to(polyline, x4, y4, ex, ey, false);
        closepath(polyline);
    } else if (cap == CAP_STYLE_SQUARE) {
        T tx = jemath::roundcast<T, jereal>(uvx * static_cast<jereal>(width));
        T ty = jemath::roundcast<T, jereal>(uvy * static_cast<jereal>(width));
        set_hole(polyline, false);
        move_to(polyline, x1 - tx, y1 - ty);
        line_to(polyline, x2 - tx, y2 - ty);
        line_to(polyline, x3 + tx, y3 + ty);
        line_to(polyline, x4 + tx, y4 + ty);
        closepath(polyline);
    } else if (cap == CAP_STYLE_FLAT) {
        set_hole(polyline, false);
        move_to(polyline, x1, y1);
        line_to(polyline, x2, y2);
        line_to(polyline, x3, y3);
        line_to(polyline, x4, y4);
        closepath(polyline);
    }

    return polyline;
}


template<typename T>
inline void pushback_vertex(Polyline<T> &polyline, const Vertex<T> &v) noexcept
{
    std::get<0>(polyline).push_back(v);
}

template<typename T>
inline void pushback_vertices(Polyline<T> &polyline, const VertexList<T> &vertices) noexcept
{
    std::get<0>(polyline).insert(std::get<0>(polyline).end(), vertices.begin(), vertices.end());
}

template<typename T>
inline void popback_vertex(Polyline<T> &polyline) noexcept
{
    std::get<0>(polyline).pop_back();
}

template<typename T>
inline void insert_vertex(Polyline<T> &polyline, size_t index, const Vertex<T> &v) noexcept
{
    std::get<0>(polyline).insert(std::get<0>(polyline).begin() + index, v);
}

template<typename T>
inline void insert_vertex(Polyline<T> &polyline, size_t index, const T &x, const T &y, jereal angle) noexcept
{
    polyline::insert_vertex(polyline, index, vertex::make_arcto(x, y, angle));
}

template<typename T>
inline void remove_vertex(Polyline<T> &polyline, size_t index, size_t count = 1) noexcept
{
    if (index >= std::get<0>(polyline).size())
        return;
    if (count == 1)
        std::get<0>(polyline).erase(std::get<0>(polyline).begin() + index);
    else
        std::get<0>(polyline).erase(std::get<0>(polyline).begin() + index, std::get<0>(polyline).begin() + index + count);
}

template<typename T>
inline void move_to(Polyline<T> &polyline, const T &x, const T &y) noexcept
{
    polyline::pushback_vertex(polyline, vertex::make_moveto(x, y));
}

template<typename T>
inline void line_to(Polyline<T> &polyline, const T &x, const T &y) noexcept
{
    polyline::pushback_vertex(polyline, vertex::make_lineto(x, y));
}

template<typename T>
inline void arc_to(Polyline<T> &polyline, const T &x, const T &y, jereal angle) noexcept
{
    polyline::pushback_vertex(polyline, vertex::make_arcto(x, y, angle));
}

template<typename T>
inline void arc_to(Polyline<T> &polyline, const T &xe, const T &ye, const T &xc, const T &yc, bool cw) noexcept
{
    auto vertices = std::get<0>(polyline);
    if (vertices.empty())
        return;
    const auto &lastv = vertices.back();
    const auto xs = std::get<0>(lastv);
    const auto ys = std::get<1>(lastv);
    if (point::is_equal<T>(xs, ys, xe, ye)) {
        polyline::pushback_vertex(polyline, vertex::make_arcto(xc, yc, 360));
    } else {
        jereal ang = jemath::arc_degree_from_secw(xs, ys, xe, ye, xc, yc, cw);
        polyline::pushback_vertex(polyline, vertex::make_arcto(xe, ye, ang));
    }
}

template<typename T>
inline bool is_closed(const Polyline<T> &polyline) noexcept
{
    auto vertices = std::get<0>(polyline);
    if (vertices.size() < 2)
        return false;
    const auto &firstv = vertices.front();
    const auto &lastv = vertices.back();
    return vertex::pos_is_equal(firstv, lastv, static_cast<T>(0));
}

template<typename T>
inline bool is_circle(const Polyline<T> &polyline) noexcept
{
    auto vertices = std::get<0>(polyline);
    return vertices.size() == 2 && vertex::is_circle(vertices.back());
}

template<typename T>
inline void closepath(Polyline<T> &polyline) noexcept
{
    auto &vertices = std::get<0>(polyline);
    if (vertices.size() < 2 || vertex::is_circle(vertices.back()))
        return;

    const auto &firstv = vertices.front();
    const auto &lastv = vertices.back();
    if (vertex::pos_not_equal(firstv, lastv, static_cast<T>(0))) {
        vertices.push_back(vertex::make_lineto(std::get<0>(firstv), std::get<1>(firstv)));
    }
}

template<typename T>
inline bool is_hole(const Polyline<T> &polyline) noexcept
{
    return std::get<1>(polyline);
}

template<typename T>
inline void set_hole(Polyline<T> &polyline, bool hole) noexcept
{
    std::get<1>(polyline) = hole;
}

template<typename T>
inline void clear(Polyline<T> &polyline) noexcept
{
    std::get<0>(polyline).clear();
}

template<typename T>
inline bool is_empty(const Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).empty();
}

template<typename T>
inline size_t verteics_count(const Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).size();
}

template<typename T>
inline const VertexList<T> &vertices(const Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline);
}

template<typename T>
inline VertexList<T> &vertices(Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline);
}

template<typename T>
inline const Vertex<T> &first_vertex(const Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).front();
}

template<typename T>
inline Vertex<T> &first_vertex(Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).front();
}

template<typename T>
inline const Vertex<T> &last_vertex(const Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).back();
}

template<typename T>
inline Vertex<T> &last_vertex(Polyline<T> &polyline) noexcept
{
    return std::get<0>(polyline).back();
}

template<typename T>
inline const Vertex<T> &vertex_at(const Polyline<T> &polyline, size_t index) noexcept
{
    return std::get<0>(polyline)[index];
}

template<typename T>
inline Vertex<T> &vertex_at(Polyline<T> &polyline, size_t index) noexcept
{
    return std::get<0>(polyline)[index];
}

template<typename T>
inline Polyline<T> to_monotoned(const Polyline<T> &polyline) noexcept
{
    auto monpoly = polyline::make<T>(polyline::is_hole(polyline));
    const auto &vertices = std::get<0>(polyline);
    if (vertices.empty())
        return monpoly;
    auto &monvertices = std::get<0>(monpoly);
    monvertices.push_back(vertices.front());
    for (size_t i = 1; i < vertices.size(); i++) {
        const auto &v = vertices[i];
        const auto &prev = monvertices.back();
        if (point::is_equal(vertex::get_pos(prev), vertex::get_pos(v))) {
            continue;
        }
        if (vertex::is_lineto(v)) {
            monvertices.push_back(v);
        } else if (vertex::is_arcto(v)) {
            T xs = std::get<0>(prev), ys = std::get<1>(prev);
            T xe = std::get<0>(v), ye = std::get<1>(v);
            jereal angle = std::get<2>(v);
            bool cw = angle < 0;
            T xc, yc;
            jemath::arc_center_from_angle(xs, ys, xe, ye, angle, xc, yc);
            auto monpoints = jemath::arc_monotone_points(xs, ys, xe, ye, xc, yc, cw);
            if (monpoints.empty()) {
                monvertices.push_back(v);
            } else {
                monpoints.push_back(point::make(std::get<0>(v), std::get<1>(v)));
                for (const auto &p : monpoints) {
                    xe = std::get<0>(p);
                    ye = std::get<1>(p);
                    if (point::is_equal(xs, ys, xe, ye))
                        continue;
                    jereal sa = std::atan2(yc - ys, xs - xc);
                    jereal ea = std::atan2(yc - ye, xe - xc);
                    if (cw) {
                        if (ea <= sa)
                            ea += jemath::PI_MUL_2;
                    } else {
                        if (sa <= ea)
                            sa += jemath::PI_MUL_2;
                    }
                    angle = jemath::rad2deg(sa - ea);
                    monvertices.push_back(vertex::make_arcto(xe, ye, angle));
                    xs = xe;
                    ys = ye;
                }
            }
        } else if (vertex::is_circle(v)) {
            monvertices.clear();
            T xs = std::get<0>(prev), ys = std::get<1>(prev);
            T xc = std::get<0>(v), yc = std::get<1>(v);
            T r = jemath::jehypot(xc - xs, yc - ys);
            monvertices.push_back(vertex::make_moveto(xc + r, yc));
            monvertices.push_back(vertex::make_arcto(xc, yc + r, 90.0));
            monvertices.push_back(vertex::make_arcto(xc - r, yc, 90.0));
            monvertices.push_back(vertex::make_arcto(xc, yc - r, 90.0));
            monvertices.push_back(vertex::make_arcto(xc + r, yc, 90.0));
        }
    }
    return monpoly;
}

template<typename T>
inline EdgeList<T> get_edges(const Polyline<T> &polyline, bool monotone = false) noexcept
{
    EdgeList<T> edges;
    if (monotone == false) {
        const auto &vertices = std::get<0>(polyline);
        for (size_t i = 1; i < vertices.size(); i++) {
            edges.push_back(edge::make(vertices[i - 1], vertices[i]));
        }
    } else {
        auto monpoly = polyline::to_monotoned(polyline);
        const auto &vertices = std::get<0>(monpoly);
        for (size_t i = 1; i < vertices.size(); i++) {
            edges.push_back(edge::make(vertices[i - 1], vertices[i]));
        }
    }
    return edges;
}

template<typename T>
inline Edge<T> edge_at(const Polyline<T> &polyline, size_t index) noexcept
{
    const auto &vertices = std::get<0>(polyline);
    if (index >= vertices.size() - 1)
        return Edge<T>{};
    return make_arched_segment(vertices[index], vertices[index + 1]);
}

template<typename T>
inline PointList<T> get_bound_points(const Polyline<T> &polyline) noexcept
{
    PointList<T> points;
    const auto &vertices = std::get<0>(polyline);
    if (vertices.empty())
        return points;
    T xs, ys, xe, ye;
    jereal angle;
    points.push_back(vertex::get_pos(vertices.front()));
    for (size_t i = 1; i < vertices.size(); i++) {
        const auto &v = vertices[i];
        xe = std::get<0>(v);
        ye = std::get<1>(v);
        if (vertex::is_lineto(v)) {
            points.push_back(point::make(xe, ye));
        } else if (vertex::is_arcto(v)) {
            const auto &pv = vertices[i - 1];
            xs = std::get<0>(pv);
            ys = std::get<1>(pv);
            angle = std::get<2>(v);
            auto pts = jemath::arc_bound_points(xs, ys, xe, ye, angle);
            points.insert(points.end(), pts.begin(), pts.end());
        } else if (vertex::is_circle(v)) {
            const auto &pv = vertices[i - 1];
            xs = std::get<0>(pv);
            ys = std::get<1>(pv);
            T r = jemath::jehypot<T>(xe - xs, ye - ys);
            points.push_back(point::make(xe - r, ye - r));
            points.push_back(point::make(xe + r, ye + r));
        }
    }
    return points;
}

template<typename T>
inline Rect<T> get_bound_rect(const Polyline<T> &polyline) noexcept
{
    auto points = polyline::get_bound_points(polyline);
    return rect::make_from_points(points);
}
} // namespace polyline

/****** archedpolygon functions *******/
namespace polygon {
template<typename T>
inline Polygon<T> make() noexcept
{
    return PolylineList<T>();
}

template<typename T>
inline Polygon<T> make(const PolylineList<T> &polyline) noexcept
{
    return PolylineList<T>{polyline};
}

template<typename T>
inline Polygon<T> make_from_rect(const T &x1, const T &y1, const T &x2, const T &y2) noexcept
{
    Polygon<T> polygon;
    add_rect(polygon, x1, y1, x2, y2, false);
    return polygon;
}

template<typename T>
inline Polygon<T> make_from_circle(const T &cx, const T &cy, const T &r, bool cw) noexcept
{
    Polygon<T> polygon;
    add_circle(polygon, cx, cy, r, cw, false);
    return polygon;
}

template<typename T>
inline void add_subpath(Polygon<T> &polygon, const Polyline<T> &path) noexcept
{
    polygon.push_back(path);
}

template<typename T>
inline void add_subpaths(Polygon<T> &polygon, const PolylineList<T> &paths) noexcept
{
    polygon.insert(polygon.end(), paths.begin(), paths.end());
}

template<typename T>
inline void subpaths_count(Polygon<T> &polygon) noexcept
{
    polygon.size();
}

template<typename T>
inline const Polyline<T> &get_subpath(const Polygon<T> &polygon, size_t index) noexcept
{
    return polygon[index];
}

template<typename T>
inline Polyline<T> &get_subpath(Polygon<T> &polygon, size_t index) noexcept
{
    return polygon[index];
}

template<typename T>
inline void clear(Polygon<T> &polygon) noexcept
{
    polygon.clear();
}

template<typename T>
inline bool is_empty(const Polygon<T> &polygon) noexcept
{
    return polygon.empty();
}

template<typename T>
inline void move_to(Polygon<T> &polygon, const T &x, const T &y, bool ishole = false) noexcept
{
    Polyline<T> polyline = polyline::make<T>();
    jegeo::polyline::set_hole(polyline, ishole);
    jegeo::polyline::move_to(polyline, x, y);
    jegeo::polygon::add_subpath(polygon, polyline);
}

template<typename T>
inline void line_to(Polygon<T> &polygon, const T &x, const T &y) noexcept
{
    if (polygon.empty())
        return;
    polyline::line_to(polygon.back(), x, y);
}

template<typename T>
inline void arc_to(Polygon<T> &polygon, const T &x, const T &y, jereal angle) noexcept
{
    if (polygon.empty())
        return;
    polyline::arc_to(polygon.back(), x, y, angle);
}

template<typename T>
inline void arc_to(Polygon<T> &polygon, const T &xe, const T &ye, const T &xc, const T &yc, bool cw) noexcept
{
    if (polygon.empty())
        return;
    polyline::arc_to(polygon.back(), xe, ye, xc, yc, cw);
}

template<typename T>
inline void close_subpath(Polygon<T> &polygon) noexcept
{
    if (polygon.empty())
        return;
    polyline::closepath(polygon.back());
}

template<typename T>
inline PointList<T> get_bound_points(const Polygon<T> &polygon) noexcept
{
    PointList<T> points;
    for (const auto &polyline : polygon) {
        auto pts = polyline::get_bound_points(polyline);
        points.insert(points.end(), pts.begin(), pts.end());
    }
    return points;
}

template<typename T>
inline Rect<T> get_bound_rect(const Polygon<T> &polygon) noexcept
{
    auto points = polygon::get_bound_points(polygon);
    return rect::make_from_points(points);
}

template<typename T>
inline void add_rect(Polygon<T> &polygon, const T &x1, const T &y1, const T &x2, const T &y2, bool ishole) noexcept
{
    auto polyline = polyline::make_rect(x1, x2, y1, y2, ishole);
    polygon::add_subpath(polygon, polyline);
}

template<typename T>
inline void add_circle(Polygon<T> &polygon, const T &cx, const T &cy, const T &r, bool cw, bool ishole) noexcept
{
    auto polyline = polyline::make_circle(cx, cy, r, cw, ishole);
    polygon::add_subpath(polygon, polyline);
}

template<typename T>
inline Polygon<T> to_monotoned(const Polygon<T> &polygon) noexcept
{
    Polygon<T> monpoly;
    for (const auto &polyline : polygon) {
        auto monpolyline = polyline::to_monotoned(polyline);
        polygon::add_subpath(monpoly, monpolyline);
    }
    return monpoly;
}

template<typename T>
inline EdgeList<T> get_edges(const Polygon<T> &polygon, bool monotone = false) noexcept
{
    EdgeList<T> edges;
    for (const auto &polyline : polygon) {
        auto segs = polyline::get_edges(polyline, monotone);
        edges.insert(edges.end(), segs.begin(), segs.end());
    }
    return edges;
}


template<typename T>
inline Polygon<T> make_from_linestoker(const Line<T> &line, const T &half_width, int capstyle)
{
    Polygon<T> polygon;
    polygon.push_back(polyline::make_from_linestoker(line, half_width, capstyle));
    return polygon;
}


template<typename T>
Polygon<T> make_from_arcstoker(const Arc<T> &arc, const T &half_width, int capstyle, bool consider_full_circle = true)
{
    Polygon<T> res;
    T arc_r = arc::get_radius(arc);
    if (jegeo::point::is_equal(jegeo::arc::get_ps(arc), jegeo::arc::get_pe(arc))) {
        // 起点和终点相同
        if (consider_full_circle) {
            T cx = jegeo::pcx(arc); T cy = jegeo::pcy(arc);
            add_circle(res, cx, cy, arc_r + half_width, jegeo::arc::get_cw(arc), false);
            if (arc_r > half_width) {
                add_circle(res, cx, cy, arc_r - half_width, !jegeo::arc::get_cw(arc), true);
            }
            return res;
        }
        else {
            T cx = jegeo::psx(arc); T cy = jegeo::psy(arc);
            T r = half_width;
            add_circle(res, cx, cy, r, jegeo::arc::get_cw(arc), false);
            return res;
        }
    }
    else {

        T psx = std::get<0>(arc);
        T psy = std::get<1>(arc);
        T pex = std::get<2>(arc);
        T pey = std::get<3>(arc);
        T pcx = std::get<4>(arc);
        T pcy = std::get<5>(arc);
        bool cw = std::get<6>(arc);

        // 求圆心到起点和终点的圆的交点
        T isx1, isy1, isx2, isy2;
        jemath::intersection_line2circle(psx, psy, pcx, pcy, psx, psy, half_width, isx1, isy1, isx2, isy2);
        if (jemath::sqrdist(isx1, isy1, pcx, pcy) > jemath::sqrdist(isx2, isy2, pcx, pcy)) {
            std::swap(isx1, isx2);
            std::swap(isy1, isy2);
        }

        T iex1, iey1, iex2, iey2;
        jemath::intersection_line2circle(pex, pey, pcx, pcy, pex, pey, half_width, iex1, iey1, iex2, iey2);
        if (jemath::sqrdist(iex1, iey1, pcx, pcy) > jemath::sqrdist(iex2, iey2, pcx, pcy)) {
            std::swap(iex1, iex2);
            std::swap(iey1, iey2);
        }

        if (capstyle == CAP_STYLE_ROUND) {
            // 判断圆弧是优弧还是劣弧
            jereal ang_rad=  jemath::arc_radian_from_secw(psx, psy, pex, pey, pcx, pcy, cw);
            if (std::abs(ang_rad) > M_PI) {
                T ix1, iy1, ix2, iy2;
                int itn = jemath::intersection_circle2circle(psx, psy, half_width, pex, pey, half_width, ix1, iy1, ix2, iy2);
                if (itn == 2) {
                    jereal sqd1 = jemath::sqrdist(ix1, iy1, pcx, pcy);
                    jereal sqd2 = jemath::sqrdist(ix2, iy2, pcx, pcy);
                    if (sqd1 > sqd2) {
                        std::swap(ix1, ix2);
                        std::swap(iy1, iy2);
                    }
                    // 外轮廓
                    move_to(res, ix2, iy2, false);
                    arc_to(res, isx2, isy2, psx, psy, cw);
                    arc_to(res, iex2, iey2, pcx, pcy, cw);
                    arc_to(res, ix2, iy2, pex, pey, cw);

                    if (half_width < arc_r) {
                        // 内轮廓
                        move_to(res, ix1, iy1, true);
                        arc_to(res, isx1, isy1, psx, psy, !cw);
                        arc_to(res, iex1, iey1, pcx, pcy, cw);
                        arc_to(res, ix1, iy1, pex, pey, !cw);
                    }
                }
                else {
                    // 外轮廓
                    move_to(res, isx2, isy2, false);
                    arc_to(res, iex2, iey2, pcx, pcy, cw);
                    arc_to(res, iex1, iey1, pex, pey, cw);
                    arc_to(res, isx1, isy1, pcx, pcy, !cw);
                    arc_to(res, isx2, isy2, psx, psy, cw);
                }
            }
            else {
                if (half_width < arc_r) {
                    // 外轮廓
                    move_to(res, isx2, isy2, false);
                    arc_to(res, iex2, iey2, pcx, pcy, cw);
                    arc_to(res, iex1, iey1, pex, pey, cw);
                    arc_to(res, isx1, isy1, pcx, pcy, !cw);
                    arc_to(res, isx2, isy2, psx, psy, cw);
                }
                else {
                    T ix1, iy1, ix2, iy2;
                    int itn = jemath::intersection_circle2circle(psx, psy, half_width, pex, pey, half_width, ix1, iy1, ix2, iy2);
                    if (itn == 2) {
                        jereal sqd1 = jemath::sqrdist(ix1, iy1, pcx, pcy);
                        jereal sqd2 = jemath::sqrdist(ix2, iy2, pcx, pcy);
                        if (sqd1 > sqd2) {
                            std::swap(ix1, ix2);
                            std::swap(iy1, iy2);
                        }
                        // 外轮廓
                        move_to(res, ix2, iy2, false);
                        arc_to(res, isx2, isy2, psx, psy, cw);
                        arc_to(res, iex2, iey2, pcx, pcy, cw);
                        arc_to(res, ix2, iy2, pex, pey, cw);
                    }
                }
            }

        }
        else {
            if (half_width < arc_r) {
                move_to(res, isx2, isy2, false);
                arc_to(res, iex2, iey2, pcx, pcy, cw);
                line_to(res, iex1, iey1);
                arc_to(res, isx1, isy1, pcx, pcy, !cw);
                line_to(res, isx2, isy2);
            }
            else {
                move_to(res, isx2, isy2, false);
                arc_to(res, iex2, iey2, pcx, pcy, cw);
                line_to(res, isx2, isy2);
            }
        }
    }
    return res;
}


} // namespace polygon

} // namespace jegeo

#endif // JEGEO_COMMON_H
